'Brook's 1337 Parser, Experimental Edition
'Last Editied: October 30, 2007

Imports System.IO
Imports Gabbscript.FunctionCaller


Module Parser
    Dim scriptList As ArrayList = New ArrayList()

    Enum ArgType
        Str = 1
        Num
        Var
        Unk
    End Enum

    Class Argument
        'Private variables
        Private m_data As Object
        Private type As ArgType
        Private scriptID As Integer

        'New function
        Public Sub New(ByVal val As Object, ByVal parent As Integer)
            Me.m_data = val
            Me.scriptID = parent
        End Sub

        'Doubke get function
        Public Shared Narrowing Operator CType(ByVal arg As Argument) As Double
            If arg.type = ArgType.Var Then
                Return scriptList.Item(arg.scriptID).GetVariable(arg.m_data)
            Else
                Return Val(arg.m_data)
            End If
        End Operator

        'Integer get function
        Public Shared Narrowing Operator CType(ByVal arg As Argument) As Integer
            Return Int(Val(arg.m_data))
        End Operator

        'String get function
        Public Shared Narrowing Operator CType(ByVal arg As Argument) As String
            If arg.type = ArgType.Var Then
                Return scriptList.Item(arg.scriptID).GetVariable(arg.m_data)
            Else
                Return arg.m_data
            End If
        End Operator

        'String get function 2
        Public Overloads Function ToString() As String
            If Me.type = ArgType.Var Then
                Return scriptList.Item(Me.scriptID).GetVariable(Me.m_data)
            Else
                Return Me.m_data
            End If
        End Function

        'Variable setting function
        Public Property Data() As Object
            Set(ByVal value)

                'If we are a variable
                If Me.type = ArgType.Var Then
                    'Get our parent script using our stored ID
                    Dim s As Script = scriptList.Item(Me.scriptID)

                    'Set our variable, to it's data
                    s.SetVariable(Me.m_data, value)
                Else
                    'Just set our data to it's data
                    Me.m_data = value
                End If
            End Set
            Get
                If Me.type = ArgType.Var Then
                    Dim s As Script = scriptList.Item(Me.scriptID)
                    Return s.GetVariable(Me.m_data)
                Else
                    Return Me.m_data
                End If
            End Get
        End Property

        'Gets the ArgType for this class
        Public Sub SetType(ByVal t As ArgType)
            Me.type = t
        End Sub

        'Math Operators

        'Addition Operator
        Public Shared Operator +(ByVal arg1 As Argument, ByVal arg2 As Argument) As Argument
            Dim valu As Double = Val(arg1.Data) + Val(arg2.Data)
            Dim a As Argument = New Argument(valu, arg1.scriptID)
            a.SetType(ArgType.Num)
            Return a
        End Operator

        'Subtraction Operator
        Public Shared Operator -(ByVal arg1 As Argument, ByVal arg2 As Argument) As Argument
            Dim valu As Double = Val(arg1.Data) - Val(arg2.Data)
            Dim a As Argument = New Argument(valu, arg1.scriptID)
            a.SetType(ArgType.Num)
            Return a
        End Operator

        'Multiplication Operator
        Public Shared Operator *(ByVal arg1 As Argument, ByVal arg2 As Argument) As Argument
            Dim valu As Double = Val(arg1.Data) * Val(arg2.Data)
            Dim a As Argument = New Argument(valu, arg1.scriptID)
            a.SetType(ArgType.Num)
            Return a
        End Operator

        'Division Operator
        Public Shared Operator /(ByVal arg1 As Argument, ByVal arg2 As Argument) As Argument
            Dim valu As Double = Val(arg1.Data) / Val(arg2.Data)
            Dim a As Argument = New Argument(valu, arg1.scriptID)
            a.SetType(ArgType.Num)
            Return a
        End Operator
    End Class

    'Structure for holding individual script instructions
    Structure Instruction
        Public cmd As String
        Public args As ArgList
    End Structure


    Class Script


        'A list of all lines of code
        Private code As ArrayList

        'Weither the script is loaded
        Private isLoaded As Boolean

        'A list of all instructions
        Private instructions As ArrayList

        'Current line of code
        Private instructionPointer As Integer

        'Script variable list
        Private variables As Hashtable

        'List of functions
        Public funcList As FunctionList

        'ID
        Public scriptID As Integer


        'Load all the lines of a file into a string array
        Public Sub LoadFile(ByVal filename As String)

            ' Declarations
            Dim file As StreamReader = New StreamReader(filename)

            code = New ArrayList()

            Dim lines As Integer = GetLines(filename)
            Dim i As Integer

            ' Adds the text of the file to an array
            For i = 1 To lines
                code.Add(file.ReadLine)
            Next
            isLoaded = True
        End Sub

        'Make a new script :)
        Public Sub New()
            funcList = New FunctionList()
            variables = New Hashtable()
            instructions = New ArrayList()
            code = New ArrayList()
            scriptList.Add(Me)
            Me.scriptID = scriptList.IndexOf(Me)
        End Sub

        'Parses a file for instructions
        Public Sub ParseFile()
            'If we have not loaded the file yet, do not allow it to be parsed
            If Not isLoaded Then Exit Sub

            instructions = New ArrayList()
            Dim i As Integer = 0

            'For each line of code
            For i = 0 To code.Count - 1

                'Get the string line of code
                Dim line As String = code.Item(i)
                Dim inst As Instruction = New Instruction()

                'Find the first index of a / character
                Dim firstSpace As Integer = line.IndexOf("/")

                'If there is one
                If firstSpace > -1 Then
                    'The command will be the text before the slash
                    inst.cmd = line.Substring(0, firstSpace).ToLower()

                    'Send the rest of the string for parsing
                    inst.args = ParseArguments(line.Substring(firstSpace))
                Else
                    'Otherwise it is just a command with no args, simply set the cmd variable
                    inst.cmd = line
                End If

                'Add the parsed instruction to the scripts instruction array!
                instructions.Add(inst)
            Next

        End Sub

        'Parses a string for arguments
        Private Function ParseArguments(ByVal argString As String) As ArgList
            'Get a local copy of the string
            Dim parseString As String = argString

            'Here is where the resulting arguments will be placed
            Dim args As ArgList = New ArgList()

            'Loop until all arguments are parsed
            Do While parseString.Contains("/")
                Dim tempString As String = parseString.Substring(1)
                'If there is another slash
                If tempString.Contains("/") Then
                    'Find it's position
                    Dim pos As Integer = tempString.IndexOf("/")
                    'Add the text between the slashes to the array
                    Dim arg As Argument = New Argument(tempString.Substring(0, pos), Me.scriptID)

                    'Set the variable type
                    arg.SetType(GetVariableType(tempString.Substring(0, pos)))

                    args.Add(arg)
                    'Set a new parseString
                    parseString = parseString.Substring(pos + 1)
                Else
                    'This is the last argument
                    Dim arg As Argument = New Argument(parseString.Substring(1), Me.scriptID)
                    arg.SetType(GetVariableType(parseString.Substring(1)))
                    args.Add(arg)
                    parseString = "Hello"
                End If
            Loop
            Return args
        End Function

        'Gets a variable by name
        Public Function GetVariable(ByVal varName As String)
            Return variables.Item(varName)
        End Function

        'Sets a variable by name
        Public Sub SetVariable(ByVal varName As String, ByVal value As Object)
            variables(varName) = value
        End Sub

        'Sets the current line of code
        Public Sub SetInstructionPointer(ByVal instPoint As Integer)
            instructionPointer = instPoint - 1
        End Sub

        'Get the current line of code
        Public Function GetInstructionPointer() As Integer
            Return Me.instructionPointer
        End Function

        'Runs the script
        Public Sub Run()
            If Not isLoaded Then Exit Sub

            instructionPointer = 0
            variables = New Hashtable()

            Do While (instructionPointer < instructions.Count And instructionPointer > -1)
                'Get current instruction
                Dim inst As Instruction = instructions.Item(instructionPointer)

                If Not funcList.CallFunction(inst.cmd, inst.args, Me) Then
                    Console.WriteLine("Error on line " & instructionPointer & " of the file")
                End If

                'Increment Instruction Pointer
                instructionPointer += 1
            Loop
        End Sub

        'Finds out how many lines are in the document
        Private Function GetLines(ByVal filename As String) As Integer

            ' Declarations
            Dim lines As Integer
            Dim file As StreamReader = New StreamReader(filename)

            ' Adds one to lines for each line (minimum 1)
            Do
                file.ReadLine()
                lines += 1
            Loop Until file.EndOfStream

            ' Returns the result
            Return lines

        End Function

        Private Function GetVariableType(ByVal arg As String) As ArgType
            Dim value As Double = Val(arg)
            Dim inte As Integer = Int(value)

            Dim string2 As String = Str(value)
            If string2.StartsWith(" ") Then
                string2 = string2.Substring(1)
            End If

            If arg.StartsWith("v_") Then
                Return ArgType.Var
            ElseIf Val(string2) = value Then
                Return ArgType.Num
            Else
                Return ArgType.Str
            End If

        End Function
    End Class

End Module

